package dryven.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;

import dryven.reflection.DeclaringMethodIterator;

public class BeanUtil {
	public String getPropertyName(Method m) {
		String propName = m.getName();
		if(propName.startsWith("get")) {
			propName = propName.substring(3);
		} else if ((m.getReturnType().equals(boolean.class) || m.getReturnType().equals(Boolean.class)) && propName.startsWith("is")) {
			propName = propName.substring(2);
		}
		propName = propName.substring(0, 1).toLowerCase()+propName.substring(1);
		return propName;
	}
	
	public String getPropertyName(Field f) {
		String propName = f.getName();
		if(propName.startsWith("_")) {
			propName = propName.substring(1);
		}
		propName = propName.substring(0, 1).toLowerCase()+propName.substring(1);
		return propName;
	}
	
	public <T extends Annotation> T getAnnotation(Class<?> declaringType, Class<T> annotationType) {
		while(declaringType!=Object.class && declaringType!=null) {
			T anno = declaringType.getAnnotation(annotationType);
			if(anno!=null) {
				return anno;
			}
			declaringType = declaringType.getSuperclass();
		}
		return null;
	}
	
	public Integer getModelPK(Object model) {
		if(model==null) {
			throw new NullPointerException();
		}
		Entity e = getAnnotation(model.getClass(),Entity.class);
		if (e == null) {
			throw new IllegalArgumentException("not a jpa entity");
		}
		Method m = getPKGetter(model.getClass());
		if(m!=null) {
			try {
				Integer i = (Integer) m.invoke(model, new Object[0]);
				if(i==null || i.intValue()==0) {
					return null;
				}
				return i;
			} catch (Exception e1) {
				throw new WrappedException(e1);
			}
		} else {
			throw new IllegalArgumentException("no @"+Id.class.getSimpleName()+" annotation found on any method for this class or any superclasses");
		}
	}
	
	public Method getPKGetter(Class<?> c) {
		for (Method m : new DeclaringMethodIterator(c, true)) {
			if (m.getAnnotation(Id.class) != null) {
				return m;
			}
		}
		return null;
	}
	
	public String getJPADatabaseFieldName(Method m) {
		Column c = m.getAnnotation(Column.class);
		if(c==null) {
			JoinColumn jc = m.getAnnotation(JoinColumn.class);
			if(jc!=null) {
				return jc.name();
			}
			return getPropertyName(m);
		} else {
			return c.name();
		}
	}
	
	public boolean isJPAColumn(Method method) {
		return method.getReturnType()!=Void.TYPE &&
			!method.isAnnotationPresent(Transient.class) && 
			method.getParameterTypes().length == 0 &&
			!method.isAnnotationPresent(OneToMany.class) &&
			!method.isAnnotationPresent(ManyToMany.class);
	}
	
	public Method[] getColumnGettersForType(Class<?> type) {
		ArrayList<Method> columns = new ArrayList<Method>();
		Method[] methods = type.getDeclaredMethods();
		
		for (Method method : methods) {
			if(isJPAColumn(method)) {
				columns.add(method);
			}
		}
		
		return columns.toArray(new Method[]{});
	}
	
	public String getTableName(Class<?> c) {
		Table t = c.getAnnotation(Table.class);
		if(t==null) {
			return c.getSimpleName();
		} else {
			return t.name();
		}
	}
	
	public boolean isJPAEntity(Class<?> type) {
		return getAnnotation(type, Entity.class)!=null;
	}
}
